//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package project_dao

import (
	"context"
	"fmt"

	"atomgit.com/openlinksaas-org/api-server/dao/db_ns"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/project_issue_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
)

type IssueStateDbItem struct {
	Dirty          bool   `bson:"dirty"`
	CreateCount    uint32 `bson:"createCount"`
	UnExecCount    uint32 `bson:"unExecCount"`
	UnCheckCount   uint32 `bson:"unCheckCount"`
	ExecDoneCount  uint32 `bson:"execDoneCount"`
	CheckDoneCount uint32 `bson:"checkDoneCount"`
}

type MemberStateDbItem struct {
	MemberId       string           `bson:"_id"`
	ProjectId      string           `bson:"projectId"`
	MemberUserId   string           `bson:"memberUserId"`
	TaskIssueState IssueStateDbItem `bson:"taskIssueState"`
	BugIssueState  IssueStateDbItem `bson:"bugIssueState"`
}

func (item *MemberStateDbItem) ToMemberState() *project_issue_api.MemberState {
	return &project_issue_api.MemberState{
		ProjectId:          item.ProjectId,
		MemberUserId:       item.MemberUserId,
		TaskCreateCount:    item.TaskIssueState.CreateCount,
		TaskUnExecCount:    item.TaskIssueState.UnExecCount,
		TaskUnCheckCount:   item.TaskIssueState.UnCheckCount,
		TaskExecDoneCount:  item.TaskIssueState.ExecDoneCount,
		TaskCheckDoneCount: item.TaskIssueState.CheckDoneCount,
		BugCreateCount:     item.BugIssueState.CreateCount,
		BugUnExecCount:     item.BugIssueState.UnExecCount,
		BugUnCheckCount:    item.BugIssueState.UnCheckCount,
		BugExecDoneCount:   item.BugIssueState.ExecDoneCount,
		BugCheckDoneCount:  item.BugIssueState.CheckDoneCount,
	}
}

type _MemberStateDao struct {
	mc *mongo.Collection
}

func newMemberStateDao(db *mongo.Database) (*_MemberStateDao, error) {
	mc := db.Collection(db_ns.DB_NS_PROJECT + "member_state")
	_, err := mc.Indexes().CreateOne(context.Background(), mongo.IndexModel{
		Keys: bson.D{{"projectId", 1}},
	})
	if err != nil {
		return nil, err
	}
	return &_MemberStateDao{mc: mc}, nil
}

func (db *_MemberStateDao) Insert(ctx context.Context, dbItem *MemberStateDbItem) error {
	dbItem.MemberId = db.genMemberId(dbItem.ProjectId, dbItem.MemberUserId)
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_MemberStateDao) Exit(ctx context.Context, projectId, userId string) (bool, error) {
	memberId := db.genMemberId(projectId, userId)
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": memberId, "projectId": projectId})
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

func (db *_MemberStateDao) Get(ctx context.Context, projectId, userId string) (*MemberStateDbItem, error) {
	memberId := db.genMemberId(projectId, userId)
	retItem := &MemberStateDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": memberId, "projectId": projectId}).Decode(retItem)
	return retItem, err
}

func (db *_MemberStateDao) ListById(ctx context.Context, projectId string, userIdList []string) ([]*MemberStateDbItem, error) {
	memberIdList := []string{}
	for _, userId := range userIdList {
		memberIdList = append(memberIdList, db.genMemberId(projectId, userId))
	}
	cursor, err := db.mc.Find(ctx, bson.M{"_id": bson.M{"$in": memberIdList}, "projectId": projectId})
	if err != nil {
		return nil, err
	}
	retList := []*MemberStateDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_MemberStateDao) UpdateTaskIssueState(ctx context.Context, projectId, userId string, dbItem *IssueStateDbItem) error {
	memberId := db.genMemberId(projectId, userId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": memberId, "projectId": projectId}, bson.M{"$set": bson.M{"taskIssueState": dbItem}})
	return err
}

func (db *_MemberStateDao) UpdateBugIssueState(ctx context.Context, projectId, userId string, dbItem *IssueStateDbItem) error {
	memberId := db.genMemberId(projectId, userId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": memberId, "projectId": projectId}, bson.M{"$set": bson.M{"bugIssueState": dbItem}})
	return err
}

func (db *_MemberStateDao) genMemberId(projectId, userId string) string {
	return fmt.Sprintf("%s:%s", projectId, userId)
}

func (db *_MemberStateDao) MarkIssueStateDirty(ctx context.Context, projectId string, issueType project_issue_api.ISSUE_TYPE, dirtyUserIdList []string) error {
	userMap := map[string]bool{}
	for _, dirtyUserId := range dirtyUserIdList {
		if dirtyUserId != "" {
			userMap[dirtyUserId] = true
		}
	}
	memberIdList := []string{}
	for userId, _ := range userMap {
		memberIdList = append(memberIdList, db.genMemberId(projectId, userId))
	}
	if len(memberIdList) == 0 {
		return nil
	}
	query := bson.M{"_id": bson.M{"$in": memberIdList}, "projectId": projectId}
	if issueType == project_issue_api.ISSUE_TYPE_ISSUE_TYPE_BUG {
		_, err := db.mc.UpdateMany(ctx, query, bson.M{"$set": bson.M{"bugIssueState.dirty": true}})
		return err
	} else if issueType == project_issue_api.ISSUE_TYPE_ISSUE_TYPE_TASK {
		_, err := db.mc.UpdateMany(ctx, query, bson.M{"$set": bson.M{"taskIssueState.dirty": true}})
		return err
	}
	return nil
}
